import { isNil, isObject } from 'lodash-es'
import { appConfig } from '../config/app';
import { getCookie, setCookie } from '../cookie'
import { BaseType } from '../index';
import { serverTime } from '../server'

//使用cookie模拟localstorage实现session功能
export const MOCK_SESSION_EXPIRE_KEY = '$mock$'

export class StorageError extends Error {
  constructor(message: string) {
    super(message)
  }
}

export class StorageExpireError extends StorageError {
}

export interface StorageOptions {
  global?: boolean //获取时如果过期自动移除
  never?: boolean //获取时如果过期抛异常
  remove?: boolean //只要不过期永远存在（点击退出都存在），相反为false时即使没有过期点击 调用 logout 也会清除缓存
  throw?: boolean //全局和当前应用有关
}

export interface StorageValue extends StorageOptions {
  expire?: number // -1:永不过期 0:session
  value?: any
}

const getKey = (key: string, options?: StorageOptions): string => {
  if (!key) {
    console.error('key 不能为空')
    key = ''
  }
  if (options?.global) {
    return `@${key}`
  } else {
    return `@${key}@${appConfig.app.alias}@${appConfig.app.version}`
  }
}

/**
 *
 * @param key
 * @param value
 * @param expire 毫秒
 * @param options
 */
export const setStorage = (key: string, value: boolean | BaseType | object | [], expire = 0, options?: StorageOptions): void => {
  if (isNil(expire)) {
    expire = 0
  }
  if (expire === 0) {
    setCookie(MOCK_SESSION_EXPIRE_KEY, 1)
  } else if (expire > 0) {
    expire = expire + serverTime.value
  }
  const json = options
    ? {
      value,
      expire,
      ...options,
    }
    : {
      value,
      expire,
    }
  localStorage.setItem(getKey(key, options), JSON.stringify(json))
}

export const getStorage = <T = any>(key: string, defaultValue: T, options?: StorageOptions): T => {
  key = getKey(key, options)
  const str = localStorage.getItem(key)
  if (str === null || str === 'null' || str === 'undefined') {
    return defaultValue as T
  }
  let json = null
  try {
    json = JSON.parse(str)
    if (!isObject(json)) {
      localStorage.removeItem(key)
      return defaultValue as T
    }
  } catch {
    return defaultValue as T
  }
  //@ts-ignore
  if (json.expire > 0 && json.expire < (serverTime.value / 1000)) {
    if (options?.remove) {
      removeStorage(key)
    }
    if (options?.throw) {
      throw new StorageExpireError('浏览器存储数据已过期')
    } else {
      return defaultValue as T
    }
  } else {
    //@ts-ignore
    return json.value
  }
}

/**
 * 获取 storage 如果过期删除存在的值
 * @param key
 * @param defaultValue
 * @param options
 */
export const getStorageIfExpireRemove = <T = any>(key: string, defaultValue: T, options?: StorageOptions): T =>
  getStorage<T>(key, defaultValue, { ...options, remove: true })

/**
 * 获取 storage 如果过期抛出异常，并且删除存在的值
 * @param key
 * @param defaultValue
 * @param options
 */
export const getStorageIfExpireThrow = <T = any>(key: string, defaultValue: T, options?: StorageOptions): T =>
  getStorage(key, defaultValue, { ...options, remove: true, throw: true })

export const allStorages = (): [] => {
  const out: any = []
  Object.keys(localStorage).forEach(key => {
    if (key.includes('@')) {
      const str = localStorage.getItem(key)
      if (str !== null) {
        const json = JSON.parse(str)
        json.key = key
        out.push(json)
      }
    }
  })
  return out
}

export const removeStorage = (key: string, global?: boolean): void => localStorage.removeItem(getKey(key, { global }))

export const clearAllStorages = (): void => localStorage.clear()

export const clearStorages = (): void => {
  Object.keys(localStorage).forEach(key => {
    if (key.includes('@')) {
      try {
        const json = localStorage.getItem(key)
        const cache: StorageOptions = JSON.parse(String(json))
        cache.never || localStorage.removeItem(key)
      } catch (e) {
        console.error(e)
      }
    }
  })
}

/**
 * 清除所有过期时间为 0 的数据(通过cookie 实现 session 的功能)
 */
export const clearExpireStorages = (): void => {
  for (const [key] of Object.entries(localStorage)) {
    if (key.includes('@')) {
      const json = localStorage.getItem(key)
      try {
        const { expire } = JSON.parse(json as string)
        if (expire === 0 && !getCookie(MOCK_SESSION_EXPIRE_KEY)) {
          localStorage.removeItem(key)
        } else if (expire > 0 && expire < serverTime.value) {
          localStorage.removeItem(key)
        }
        const arr = key.split('@')
        if (arr.length === 4) {
          const version = parseInt(arr[3])
          if (version < appConfig.app.version) {
            localStorage.removeItem(key)
          }
        }
      } catch {
      }
    }
  }
}
